using System;
using System.Collections.Generic;
using System.Text;
using System.Runtime.InteropServices;

namespace ITC_KEYBOARD
{
    /// <summary>
    /// a class to manage ModifiersKeys
    /// </summary>
    public class CModifiersKeys
    {
        /// <summary>
        /// a var to hold ModifiersKeys data
        /// </summary>
        public CUSBkeys.usbKeyStructShort ModifierKeyStruct;
        ///// <summary>
        ///// a Modifierkeystruct is similar to an USBKeyStruct except to that
        ///// it does not have an USB code page and USB HID scancode
        ///// </summary>
        //[Serializable]
        //[StructLayout(LayoutKind.Sequential)]
        //public struct ModifierKeyStruct
        //{
        //    /// <summary>
        //    /// high byte flags for the key definition
        //    /// </summary>
        //    public byte bFlagHigh;
        //    /// <summary>
        //    /// mid byte flags for the key definition
        //    /// </summary>
        //    public byte bFlagMid;
        //    /// <summary>
        //    /// low byte flags for the key definition
        //    /// </summary>
        //    public byte bFlagLow;
        //    /// <summary>
        //    /// the value generated by the key entry, a VKEY or an index into another table
        //    /// </summary>
        //    public byte bIntScan;
        //}

        /// <summary>
        /// gives a text dump reprensentation of the ModifiersKey
        /// </summary>
        /// <param name="_theBytes">the ModifierKeyStruct to dump</param>
        /// <returns>a string with the meanings of the structure</returns>
        public string dumpModifierKey(CUSBkeys.usbKeyStructShort _theBytes)
        {
            byte b;
            b = (byte)_theBytes.bFlagLow;
            string s = b.ToString("X02");
            b = (byte)_theBytes.bFlagMid;
            s += "," + b.ToString("X02");
            b = (byte)_theBytes.bFlagHigh;
            s += "," + b.ToString("X02");
            s += "," + _theBytes.bIntScan.ToString("X02");

            //getName does not care about extended etc...
            s += " '" + ITC_KEYBOARD.CUSBPS2_vals.Cusbps2key.getName(_theBytes.bIntScan) + "'";
            s += " | ";
            return s;
        }
        /// <summary>
        /// gives a text dump reprensentation of the ModifiersKeys
        /// </summary>
        /// <param name="iIdx">the index of the ModifierKeyStruct to dump</param>
        /// <returns>a string with the meanings of the structure</returns>
        public string dumpModifierKey(int iIdx)
        {
            byte[] bs = _ModifierKeyStructList[iIdx - 1];
            CUSBkeys.usbKeyStructShort rs = RawDeserialize2(bs);
            string s = "->";
            s += dumpModifierKey(rs);
            return s;
        }

        public CUSBkeys.usbKeyStructShort getModifiersKey(int idx)
        {
            int iMax = getModifierKeyCount(); 
            if (idx > iMax)
                return new CUSBkeys.usbKeyStructShort();
            return _ModifierKeyStructs[idx];

        }
        /// <summary>
        /// an array to hold ModifiersKeys as found in registry
        /// </summary>
        private CUSBkeys.usbKeyStructShort[] _ModifierKeyStructs;

        /// <summary>
        /// number of ModifiersKeys found in registry
        /// </summary>
        private int _ModifiersKeysCount = 0;
        /// <summary>
        /// a list to hold ModifiersKeys as found in registry
        /// </summary>
        private List<byte[]> _ModifierKeyStructList;

        /// <summary>
        /// initialize the ModifiersKeys lists by reading them from registry
        /// </summary>
        public CModifiersKeys()
        {
            //read number of entries
            int iCount = this.getModifierKeyCount();
            if (iCount == 0)
                throw new ArgumentNullException("Sorry, no ModifierKeys supported");
            //create new arrays
            _ModifierKeyStructList=new List<byte[]>(iCount+1);
            this.readAll();
        }
        /// <summary>
        /// converts an array of ModifierKeyStruct to a byte array for
        /// storing into registry
        /// </summary>
        /// <param name="structData">the array to convert</param>
        /// <returns>a byte array ready to save to registry</returns>
        private byte[] RawSerialize(CUSBkeys.usbKeyStructShort[] structData)
        {
            //size
            int structSize = Marshal.SizeOf(structData[0]);
            int iRawSize = structData.Length * structSize;
            byte[] rawDatas = new byte[iRawSize];
            for (int i = 0; i < structData.Length; i++)
            {
                rawDatas[(i * structSize) + 0] = (byte)structData[i].bFlagHigh;
                rawDatas[(i * structSize) + 1] = (byte)structData[i].bFlagMid;
                rawDatas[(i * structSize) + 2] = (byte)structData[i].bFlagLow;
                rawDatas[(i * structSize) + 3] = (byte)structData[i].bIntScan;
            }
            return rawDatas;
        }
        /// <summary>
        /// converts a byte arrray as read from registry
        /// to an array of ModifierKeyStruct
        /// </summary>
        /// <param name="rawData">the bytes as read from registry</param>
        /// <returns>array of ModifierKeyStruct</returns>
        private static CUSBkeys.usbKeyStructShort RawDeserialize2(byte[] rawData)
        {
            int structSize = 4;
            int iCount = rawData.Length / structSize; //we have 4 bytes per struct
            CUSBkeys.usbKeyStructShort[] _multiStruct = new CUSBkeys.usbKeyStructShort[iCount];
            for (int i = 0; i < iCount; i++)
            {
                _multiStruct[i].bFlagHigh = (CUsbKeyTypes.usbFlagsHigh)rawData[i * structSize + 0];
                _multiStruct[i].bFlagMid = (CUsbKeyTypes.usbFlagsMid)rawData[i * structSize + 1];
                _multiStruct[i].bFlagLow = (CUsbKeyTypes.usbFlagsLow)rawData[i * structSize + 2];
                _multiStruct[i].bIntScan = rawData[i * structSize + 3];
            }
            return _multiStruct[0]; //return first struct only, there should be only one struct
        }
        /// <summary>
        /// get the number of defined ModifierKeys as readable from registry
        /// </summary>
        /// <returns>the number of ModifierKeys</returns>
        public int getModifierKeyCount()
        {
            if (this._ModifiersKeysCount != 0)
                return this._ModifiersKeysCount;
            string regKeyb = CUSBkeys.getRegLocation() + @"\ModifiersKeys";
            Microsoft.Win32.RegistryKey tempKey = Microsoft.Win32.Registry.LocalMachine.OpenSubKey(regKeyb, true);
            int i = 1;
            object o = null;
            do
            {
                try
                {
                    o = tempKey.GetValue("ModKey" + i.ToString());
                }
                catch (Exception)
                {
                    o = null;
                    break;
                }
                if(o!=null)
                    i++;
            } while (o!=null);
            if (i != 0)
                return i - 1;
            else
                return 0;
        }

        private void readAll()
        {
            int iCount = this.getModifierKeyCount();
            if (iCount == 0)
                return;
            //read all ModifierKeys entries
            _ModifierKeyStructs = new CUSBkeys.usbKeyStructShort[iCount];

            string regKeyb = CUSBkeys.getRegLocation() + @"\ModifiersKeys";
            Microsoft.Win32.RegistryKey tempKey = Microsoft.Win32.Registry.LocalMachine.OpenSubKey(regKeyb, true);

            _ModifierKeyStructs = new CUSBkeys.usbKeyStructShort[iCount]; //create a new list

            for (int i = 0; i < iCount; i++)
            {
                byte[] bModifierKeys = (byte[])tempKey.GetValue("ModKey" + (i+1).ToString());
                //we will have only one keystruct in modifiers
                _ModifierKeyStructs[i] = RawDeserialize2(bModifierKeys);

                _ModifierKeyStructList.Add(bModifierKeys);
            }
            tempKey.Close();
            System.Diagnostics.Debug.WriteLine("ModifiersKeys readall finished");
        }
        public int findModifierKey(CUSBkeys.usbKeyStructShort modKey)
        {
            int iRet = -1; //not found
            int iMax = this.getModifierKeyCount();
            CUSBkeys.usbKeyStructShort mStruct;
            for (int i = 0; i < iMax; i++)
            {
                mStruct = this.getModifiersKey(i);
                if (mStruct.Equals(modKey))
                    iRet = i+1;
            }
            return iRet;
        }

        public int addModifierKey(CUSBkeys.usbKeyStructShort modKey)
        {
            int iMax = getModifierKeyCount();
            CUSBkeys.usbKeyStructShort[] bTemp = new CUSBkeys.usbKeyStructShort[1];
            bTemp[0] = modKey;
            byte[] bNew = this.RawSerialize(bTemp);
            
            //add a new multikeyentry
            string regKeyb = CUSBkeys.getRegLocation() + @"\ModifiersKeys";
            Microsoft.Win32.RegistryKey tempKey = Microsoft.Win32.Registry.LocalMachine.OpenSubKey(regKeyb, true);

            tempKey.SetValue("ModKey" + (iMax + 1).ToString(), bNew, Microsoft.Win32.RegistryValueKind.Binary);
            //reread all
            readAll();

            return iMax + 1;
        }

    }
}
